The One Arm Expected Events calculator computes an estimate of the expected events for a planned one-arm study. The program assumes uniform accrual and exponential survival. It allows the user to specify a follow-up period after the close of study accrual. The program default presents a table of expected event totals at timepoints spanning the duration of the study. Alternatively, user can calculate the expected proportion of events at a given time, or the time at which a given proportion of events have occurred. The program will allow the user to specify a percentage of patients that have no risk (‘Percent cured’).
The user is prompted for values to the following items. For items that have initial default values set, the values are given in parentheses.
For calculations of expected events at given analysis time(s):
For calculations of analysis time for a given number of expected events:
Hazard rate: The hazard rate λ for a survival probability S(t) at time t is computed as follows:
λ=−ln(S(t))t
Total probability of event: The probability Ptot of an event for a study with hazard rate λ, accrual time tacc, and follow-up time tfu, is as follows:
Ptot=1−e−λtfu(1−e−λtacc)λtacc
Fraction of the total events at time t: At times before the completion of accrual, the probability of an event at analysis time t is
P(Event|t)=Pd|tPtot At times before the completion of accrual, Pd|t is as follows:
Pd|t=(1λe−λt)+t−1λtacc
At all other times, Pd|t is as follows:
Pd|t=1−e−λ(t−tacc)(1−e−λtacc)λtacc
The program is written in R.
function(hazard, medsurv, survtime, survprob, accrual_time, fu_time, N, pct_events, survival_input, pct_cured) {
if (survival_input == "Median Survival") {
hazard = -1 * log(0.5) / medsurv
}
if (survival_input == "Survival Probability") {
hazard = -1 * log(survprob) / survtime
}
perdeath = function(hazard, accrual_time, fu_tume, time) {
ptot = 1 - exp(-1 * hazard * fu_tume) * (1 - exp(-1 * hazard * accrual_time)) / (hazard * accrual_time)
if (time < accrual_time) {
pd = ((1 / hazard) * exp(-1 * hazard * time) + time - 1 / hazard) / accrual_time
}
else {
pd = 1 - exp(-1 * hazard * (time - accrual_time)) * (1 - exp(-1 * hazard * accrual_time)) / (hazard * accrual_time)
}
return(pd / ptot)
}
aperdeath = function(hazard, accrual, follow, p) {
v = .5
dv = .5
x = 0
while (dv > 1e-6) {
x = 1 / v - 1
dv = dv / 2
if (perdeath(hazard, accrual, follow, x) > p) {
v = v + dv
} else {
v = v - dv
}
}
return(x)
}
eea = (1 - pct_cured) * N * (1 - exp(-1 * hazard * fu_time) * (1 - exp(-1 * hazard * accrual_time)) / (hazard * accrual_time))
analysis_time = aperdeath(hazard, accrual_time, fu_time, pct_events)
eet = eea * perdeath(hazard, accrual_time, fu_time, analysis_time)
result = list(hazard = signif(hazard, 2),
analysis_time = round(analysis_time, 2),
eet = floor(eet),
eea = floor(eea))
return(jsonlite::toJSON(result, pretty = TRUE))
}
function(hazard, medsurv, survtime, survprob, accrual_time, fu_time, N, analysis_time, survival_input, pct_cured) {
if (survival_input == "Median Survival") {
hazard = -1 * log(0.5) / medsurv
}
if (survival_input == "Survival Probability") {
hazard = -1 * log(survprob) / survtime
}
perdeath = function(hazard, accrual_time, fu_tume, time) {
ptot = 1 - exp(-1 * hazard * fu_tume) * (1 - exp(-1 * hazard * accrual_time)) / (hazard * accrual_time)
if (time < accrual_time) {
pd = ((1 / hazard) * exp(-1 * hazard * time) + time - 1 / hazard) / accrual_time
} else {
pd = 1 - exp(-1 * hazard * (time - accrual_time)) * (1 - exp(-1 * hazard * accrual_time)) / (hazard * accrual_time)
}
return(pd / ptot)
}
pct_eet = perdeath(hazard, accrual_time, fu_time, analysis_time)
pct_eet_100 = pct_eet * 100
eea = (1 - pct_cured) * N * (1 - exp(-1 * hazard * fu_time) * (1 - exp(-1 * hazard * accrual_time)) / (hazard * accrual_time))
eet = pct_eet * eea
result = list(hazard = signif(hazard, 2),
pct_eet_100 = round(pct_eet_100, 1),
eet = floor(eet),
eea = floor(eea))
return(jsonlite::toJSON(result, pretty = TRUE))
}
function(hazard, medsurv, survtime, survprob, accrual_time, fu_time, N, time_unit, survival_input, pct_cured) {
if (survival_input == "Median Survival") {
hazard = -1 * log(0.5) / medsurv
}
if (survival_input == "Survival Probability") {
hazard = -1 * log(survprob) / survtime
}
end_time = (accrual_time + fu_time) * (ifelse(time_unit == 'Years', 12, 1))
follow_time = (fu_time) * (ifelse(time_unit == 'Years', 12, 1))
acc_time = (accrual_time) * (ifelse(time_unit == 'Years', 12, 1))
hazard_months = hazard / ifelse(time_unit == 'Years', 12, 1)
bymonth = matrix(nrow = end_time, ncol = 5)
perdeath = function(hazard, accrual_time, fu_tume, time) {
ptot = 1 - exp(-1 * hazard * fu_tume) * (1 - exp(-1 * hazard * accrual_time)) / (hazard * accrual_time)
if (time < accrual_time) {
pd = ((1 / hazard) * exp(-1 * hazard * time) + time - 1 / hazard) / accrual_time
} else {
pd = 1 - exp(-1 * hazard * (time - accrual_time)) * (1 - exp(-1 * hazard * accrual_time)) / (hazard * accrual_time)
}
return(pd / ptot)
}
p_t = perdeath(hazard_months, acc_time, follow_time, end_time)
eea = (1 - pct_cured) * N * (1 - exp(-1 * hazard_months * follow_time) * (1 - exp(-1 * hazard_months * acc_time)) / (hazard_months * acc_time))
eet = p_t * eea
for (i in 1:end_time) {
Ni = floor(min(N, i * (N / acc_time)))
p_i = perdeath(hazard_months, acc_time, follow_time, i)
ee_i = eet * p_i
pct_i = ee_i / eet * 100
pct_accr = Ni * 100 / N
bymonth[i, ] = c(i, round(ee_i,1), round(pct_i,1), Ni, round(pct_accr,1))
}
bymonth = as.data.frame(bymonth)
names(bymonth) = c("TimeinMonths", "TotalEvents", "PercentTotalEvents", "TotalAccrual", "PercentAccrual")
bymonth$PercentTotalEvents = paste0(bymonth$PercentTotalEvents, "%")
bymonth$PercentAccrual = paste0(bymonth$PercentAccrual, "%")
result = list(hazard = signif(hazard, 2),
event_table = bymonth)
return(jsonlite::toJSON(result, pretty = TRUE))
}